package manage.modules;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import manage.commons.utils.CommonUtil;
import manage.commons.utils.DBUtils;
import manage.commons.utils.NumberAndBoolean;
import manage.commons.utils.TableNameUtil;

import org.apache.log4j.Logger;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

public class AbstractDaoImpl implements AbstractDao {
	private static final Logger log = Logger.getLogger(AbstractDaoImpl.class);
	private NamedParameterJdbcTemplate mainJdbc;
	private NamedParameterJdbcTemplate queryJdbc;
	private static final int DEFAULT_ROW_INDEX = 0;
	private static final int DEFAULT_ROW_SIZE = 100;

	@Override
	public <T> List<T> listByField(String field, Object value, Class<T> clazz) {
		return listByField(field, value, clazz, DEFAULT_ROW_INDEX, DEFAULT_ROW_SIZE);
	}

	@Override
	public <T> List<T> listByField(String field, Object value, Class<T> clazz, int rowIndex,
			int rowSize) {
		Map<String, Object> fields = new HashMap<String, Object>(1);
		fields.put(field, value);
		return listByFields(fields, clazz, rowIndex, rowSize);
	}

	@Override
	public <T> List<T> listByFields(Map<String, Object> fields, Class<T> clazz) {
		return listByFields(fields, clazz, DEFAULT_ROW_INDEX, DEFAULT_ROW_SIZE);
	}

	@Override
	public <T> List<T> listByFields(Map<String, Object> fields, Class<T> clazz, int rowIndex,
			int rowSize) {
		String conditionsExt = "limit :rowIndex, :rowSize";
		String sql = DBUtils.generateSelectSQL(getTableName(clazz), fields, conditionsExt);
		RowMapper<T> rm = ParameterizedBeanPropertyRowMapper.newInstance(clazz);
		Map<String, Object> params = new HashMap<String, Object>(fields);
		params.put("rowIndex", rowIndex);
		params.put("rowSize", rowSize);
		return getQueryJdbc().query(DBUtils.restrictSelectSQL(sql, clazz), params, rm);
	}

	@Override
	public <T> List<T> listByFieldLike(String field, Object value, Class<T> clazz) {
		return listByFieldLike(field, value, clazz);
	}

	@Override
	public <T> List<T> listByFieldRandom(String field, Object value, Class<T> clazz, int rowSize) {
		StringBuilder sb = new StringBuilder().append("select * from ").append(getTableName(clazz))
				.append(" where ").append(field).append(" = ? order by rand() limit ?");
		RowMapper<T> rm = ParameterizedBeanPropertyRowMapper.newInstance(clazz);
		return getQueryJdbc().getJdbcOperations().query(
				DBUtils.restrictSelectSQL(sb.toString(), clazz), rm, value, rowSize);
	}

	@Override
	public <T> List<T> listByFieldLike(String field, Object value, Class<T> clazz, int rowIndex,
			int rowSize) {
		StringBuilder sb = new StringBuilder().append("select * from ").append(getTableName(clazz))
				.append(" where ").append(field).append(" like ? limit ?, ?");
		RowMapper<T> rm = ParameterizedBeanPropertyRowMapper.newInstance(clazz);
		return getQueryJdbc().getJdbcOperations().query(
				DBUtils.restrictSelectSQL(sb.toString(), clazz), rm, "%" + value + "%", rowIndex,
				rowSize);
	}

	@Override
	public <T> List<T> listAll(Class<T> clazz) {
		String selectAllSql = DBUtils.generateSelectAllSQL(getTableName(clazz));
		ParameterizedRowMapper<T> rowMapper = ParameterizedBeanPropertyRowMapper.newInstance(clazz);
		return getQueryJdbc().getJdbcOperations().query(
				DBUtils.restrictSelectSQL(selectAllSql, clazz), rowMapper);
	}

	@Override
	public <T> T select(String field, Object value, Class<T> clazz) {
		Map<String, Object> conditions = new HashMap<String, Object>();
		conditions.put(field, value);
		return select(conditions, clazz);
	}

	@Override
	public <T> T select(Map<String, Object> conditions, Class<T> clazz) {
		List<T> list = listByFields(conditions, clazz);
		if (list != null && list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

	@Override
	public <T> int insert(T entity) {
		String insertSql = DBUtils.generateInsertSQL(getTableName(entity.getClass()), entity);
		log.info("insert ======>" + getTableName(entity.getClass()) + "{"
				+ CommonUtil.beanPropertisToString(entity) + "}");
		return getMainJdbc().update(insertSql, new BeanPropertySqlParameterSource(entity));
	}

	@Override
	public <T> long insertFetchId(T entity) {
		String insertSql = DBUtils.generateInsertSQL(getTableName(entity.getClass()), entity);
		KeyHolder keyHolder = new GeneratedKeyHolder();
		log.info("insert ======>" + getTableName(entity.getClass()) + "{"
				+ CommonUtil.beanPropertisToString(entity) + "}");
		getMainJdbc().update(insertSql, new BeanPropertySqlParameterSource(entity), keyHolder);
		return keyHolder.getKey().longValue();
	}

	@Override
	public <T> int replace(T entity) {
		String sql = DBUtils.generateReplaceSQL(getTableName(entity.getClass()), entity);
		log.info("replace ======>" + getTableName(entity.getClass()) + "}"
				+ CommonUtil.beanPropertisToString(entity) + "}");
		return getMainJdbc().update(sql, new BeanPropertySqlParameterSource(entity));
	}

	@Override
	public <T> int updateFields(String field, Object value, T entity) {
		@SuppressWarnings("unchecked")
		T dbEntity = (T) select(field, value, entity.getClass());
		Map<String, Object> updateMap = DBUtils.comparePojo(dbEntity, entity);
		if (updateMap == null || updateMap.size() == 0) {
			return 0;
		}
		Map<String, Object> conditions = new HashMap<String, Object>(1);
		conditions.put(field, value);
		log.info("update ======>" + getTableName(entity.getClass()) + "{"
				+ CommonUtil.beanPropertisToString(entity) + "} where " + field + "=" + value);
		return updateFields(updateMap, conditions, entity.getClass());
	}

	@Override
	public <T> int updateFields(Map<String, Object> fields, Map<String, Object> conditions,
			Class<T> clazz) {
		String conditionsExt = "limit " + DEFAULT_ROW_SIZE;
		String sql = DBUtils.generateUpdateSQL(getTableName(clazz), fields, conditions,
				conditionsExt);
		// 不支持更新条件字段
		fields.putAll(conditions);
		DBUtils.format(fields);
		log.info("update ======>" + getTableName(clazz) + fields.toString() + " where"
				+ conditions.toString() + " limit" + DEFAULT_ROW_SIZE);
		return getMainJdbc().update(sql, fields);
	}

	@Override
	public <T> int delete(String field, Object value, Class<T> clazz) {
		String deleteSql = DBUtils.generateDeleteSQL(getTableName(clazz), field);
		log.info("delete ======>" + getTableName(clazz) + " where {" + field + "=" + value + "} ");
		return getMainJdbc().update(deleteSql, new MapSqlParameterSource(field, value));
	}

	@Override
	public <T> int deleteByFields(Map<String, Object> fields, Class<T> clazz) {
		log.info("delete ======>" + getTableName(clazz) + fields.toString());
		return deleteByFields(fields, clazz, Integer.MAX_VALUE);
	}

	@Override
	public <T> int deleteByFields(Map<String, Object> fields, Class<T> clazz, int rowSize) {
		String conditionsExt = "limit :rowSize";
		String sql = DBUtils.generateDeleteSQL(getTableName(clazz), fields, conditionsExt);
		Map<String, Object> params = new HashMap<String, Object>(fields);
		params.put("rowSize", rowSize);
		log.info("delete ======>" + getTableName(clazz) + fields.toString() + " limit" + rowSize);
		return getMainJdbc().update(sql, params);
	}

	@Override
	public <T> boolean exists(String field, Object value, Class<T> clazz) {
		Map<String, Object> conditions = new HashMap<String, Object>();
		conditions.put(field, value);
		return exists(conditions, clazz);
	}

	@Override
	public <T> boolean exists(Map<String, Object> fields, Class<T> clazz) {
		String countSql = DBUtils.generateCountSQL(getTableName(clazz), fields);
		int count = getQueryJdbc().queryForInt(countSql, new MapSqlParameterSource(fields));
		return NumberAndBoolean.numberToBoolean(count);
	}

	@Override
	public <T> Long countOfAll(Class<T> clazz) {
		String countSql = DBUtils.generateCountSQL(getTableName(clazz));
		return getQueryJdbc().getJdbcOperations().queryForLong(countSql);
	}

	@Override
	public <T> Long countByFields(Map<String, Object> fields, Class<T> clazz) {
		String sql = DBUtils.generateCountSQL(getTableName(clazz), fields);
		return getQueryJdbc().queryForLong(sql, fields);
	}

	@Override
	public <T> Long countByField(String field, String value, Class<T> clazz) {
		Map<String, Object> fields = new HashMap<String, Object>(1);
		fields.put(field, value);
		return countByFields(fields, clazz);
	}

	private <T> String getTableName(Class<T> clazz) {
		return TableNameUtil.getTableName(clazz);
	}

	public void setMainJdbc(NamedParameterJdbcTemplate mainJdbc) {
		this.mainJdbc = mainJdbc;
	}

	public void setQueryJdbc(NamedParameterJdbcTemplate queryJdbc) {
		this.queryJdbc = queryJdbc;
	}

	/**
	 * 通过这个方法获得jdbc，是随机取的一台机器的查询连接 .规则可以根据需要自己切换
	 * 
	 * @return
	 */
	public NamedParameterJdbcTemplate getQueryJdbc() {
		int random = new Random().nextInt(2);
		return random % 2 == 1 ? queryJdbc : queryJdbc;
	}

	public NamedParameterJdbcTemplate getMainJdbc() {
		return mainJdbc;
	}

}
